#ifndef DAQIPC_BASEdeque_H
#define DAQIPC_BASEdeque_H

#include <mutex>
#include <deque>
#include <vector>

namespace libdaq {
template<typename T>
class BaseQueue {
public:
  BaseQueue(size_t max_size = 0) : max_size_(max_size) {}

  BaseQueue(const BaseQueue &other) : max_size_(other.max_size_) {
    deque_ = other.deque_;
  }

  BaseQueue &operator=(const BaseQueue &other) {
    if (this == &other) {
      return *this;
    }
    max_size_ = other.max_size_;
    deque_ = other.deque_;
    return *this;
  }

  BaseQueue(BaseQueue &&other) : max_size_(other.max_size_) {
    deque_ = std::move(other.deque_);
  }

  BaseQueue &operator=(BaseQueue &&other) {
    if (this == &other) {
      return *this;
    }
    max_size_ = other.max_size_;
    deque_ = std::move(other.deque_);
    return *this;
  }

  virtual ~BaseQueue() {}

//  bool emplace_back(T &&item) {
//    std::lock_guard <std::mutex> lock(mutex_);
//    if ((deque_.size() >= max_size_) && (max_size_ > 0)) {
//      return false;
//    }
//    deque_.emplace_back(std::move(item));
//    return true;
//  }
//
//  bool emplace_back(T *items, size_t size) {
//    std::lock_guard <std::mutex> lock(mutex_);
//    if ((deque_.size() >= max_size_) && (max_size_ > 0)) {
//      return false;
//    }
//    for (size_t i = 0; i < size; i++) {
//      deque_.emplace_back(std::move(items[i]));
//    }
//    return true;
//  }

  bool push(const T &item) {
    std::lock_guard <std::mutex> lock(mutex_);
    if ((deque_.size() >= max_size_) && (max_size_ > 0)) {
      return false;
    }
    deque_.push_back(item);
    return true;
  }

  bool push(T *items, size_t size) {
    std::lock_guard <std::mutex> lock(mutex_);
    if ((deque_.size() >= max_size_) && (max_size_ > 0)) {
      return false;
    }
    for (size_t i = 0; i < size; i++) {
      deque_.push_back(items[i]);
    }
    return true;
  }

  bool pop(T &item) {
    std::lock_guard <std::mutex> lock(mutex_);
    if (deque_.empty()) {
      return false;
    }
    item = deque_.front();
    deque_.pop_front();
    return true;
  }

  bool pop(T* items, size_t size) {
    std::lock_guard <std::mutex> lock(mutex_);
    if (deque_.empty()) {
      return false;
    }
    if (deque_.size() < size) {
      return false;
    }
    for (size_t i = 0; i < size; i++) {
      items[i] = std::move(deque_.front());
      deque_.pop_front();
    }
    return true;
  }

  bool pop(std::vector <T> &items, size_t size) {
    std::lock_guard <std::mutex> lock(mutex_);
    if (deque_.empty()) {
      return false;
    }
    if (deque_.size() < size) {
      return false;
    }
    for (size_t i = 0; i < size; i++) {
      items.emplace_back(deque_.front());
      deque_.pop_front();
    }
    return true;
  }

  size_t size() {
    std::lock_guard <std::mutex> lock(mutex_);
    return deque_.size();
  }

  bool empty() {
    std::lock_guard <std::mutex> lock(mutex_);
    return deque_.empty();
  }

  void clear() {
    std::lock_guard <std::mutex> lock(mutex_);
    deque_.clear();
  }

private:
  std::deque <T> deque_;
  size_t max_size_;
  std::mutex mutex_;
};
}

#endif //DAQIPC_BASEdeque_H
